//////////////////////////////////////////ok
#include"stdafx.h"

#include "bochs.h"



void  IA32_CPU::jump_protected(Ia32_Instruction_c *i, Bit16u cs_raw, ia32_address dispBig)
{
  ia32_descriptor_t  descriptor;
  ia32_selector_t    selector;
  Bit32u dword1, dword2;

  /* destination selector is not null else #GP(0) */
  if ((cs_raw & 0xfffc) == 0) 
  {
    exception(IA32_GP_EXCEPTION, 0, 0);
    return;
  }

  parse_selector(cs_raw, &selector);
  fetch_raw_descriptor(&selector, &dword1, &dword2, IA32_GP_EXCEPTION);
  parse_descriptor(dword1, dword2, &descriptor);

  if ( descriptor.segment ) 
  {
    if ( descriptor.u.segment.executable==0 ) 
	{
      exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
      return;
    }
    // CASE: JUMP CONFORMING CODE SEGMENT:
    if ( descriptor.u.segment.c_ed ) 
	{
      if (descriptor.dpl > CPL) 
	  {
        exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
        return;
      }
    }
    // CASE: jump nonconforming code segment
    else 
	{
      /* RPL of destination selector must be <= CPL else #GP(selector) */
      if (selector.rpl > CPL) 
	  {
        exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
        return;
      }

      // descriptor DPL must = CPL else #GP(selector)
      if (descriptor.dpl != CPL) 
	  {
        exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
        return;
      }
    }

    /* segment must be PRESENT else #NP(selector) */
    if (descriptor.p == 0) 
	{
      exception(IA32_NP_EXCEPTION, cs_raw & 0xfffc, 0);
      return;
    }

    /* instruction pointer must be in code segment limit else #GP(0) */
    if (dispBig > descriptor.u.segment.limit_scaled) 
	{
       exception(IA32_GP_EXCEPTION, 0, 0);
       return;
    }
    /* Load CS:IP from destination pointer */
    /* Load CS-cache with new segment descriptor */
    /* CPL does not change for conforming code segment */
    load_cs(&selector, &descriptor, CPL);
    EIP = dispBig;
    return;
  }
  else 
  {
    Bit16u          raw_tss_selector;
    ia32_selector_t   tss_selector, gate_cs_selector;
    ia32_descriptor_t tss_descriptor, gate_cs_descriptor;
    Bit16u gate_cs_raw;
    Bit32u temp_eIP;

    // descriptor DPL must be >= CPL else #GP(gate selector)
    if (descriptor.dpl < CPL) 
	{
      exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
      return;
    }

    // descriptor DPL must be >= gate selector RPL else #GP(gate selector)
    if (descriptor.dpl < selector.rpl) 
	{
      exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
      return;
    }

    switch ( descriptor.type ) 
	{
      case IA32_SYS_SEGMENT_AVAIL_286_TSS:
      case IA32_SYS_SEGMENT_AVAIL_386_TSS:
        // SWITCH_TASKS _without_ nesting to TSS
        task_switch(&selector, &descriptor, IA32_TASK_FROM_JUMP, dword1, dword2);
        if (EIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
          return;
        }
        return;
      case IA32_286_CALL_GATE:
        if (descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, cs_raw & 0xfffc, 0);
          return;
        }
        // examine selector to code segment given in call gate descriptor
        // selector must not be null, else #GP(0)
        gate_cs_raw = descriptor.u.gate286.dest_selector;
        if ((gate_cs_raw & 0xfffc) == 0) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
        }
        parse_selector(gate_cs_raw, &gate_cs_selector);
        fetch_raw_descriptor(&gate_cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
        parse_descriptor(dword1, dword2, &gate_cs_descriptor);
        // descriptor AR byte must indicate code segment else #GP(CS selector)
        if ( (gate_cs_descriptor.valid==0) || (gate_cs_descriptor.segment==0) || (gate_cs_descriptor.u.segment.executable==0) )
        {
          exception(IA32_GP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
        }

        // if non-conforming, code segment descriptor DPL must = CPL else #GP(CS selector)
        if (gate_cs_descriptor.u.segment.c_ed==0) 
		{
          if (gate_cs_descriptor.dpl != CPL) 
		  {
            exception(IA32_GP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
          }
        }
        // if conforming, then code segment descriptor DPL must <= CPL else #GP(CS selector)
        else 
		{
          if (gate_cs_descriptor.dpl > CPL) 
		  {
            exception(IA32_GP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
          }
        }

        // code segment must be present else #NP(CS selector)
        if (gate_cs_descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
        }

        // IP must be in code segment limit else #GP(0)
        if ( descriptor.u.gate286.dest_offset > gate_cs_descriptor.u.segment.limit_scaled )
        {
          exception(IA32_GP_EXCEPTION, 0, 0);
        }

        // load CS:IP from call gate
        // load CS cache with new code segment
        // set rpl of CS to CPL
        load_cs(&gate_cs_selector, &gate_cs_descriptor, CPL);
        EIP = descriptor.u.gate286.dest_offset;
        return;
      case IA32_TASK_GATE:
        // task gate must be present else #NP(gate selector)
        if (descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, cs_raw & 0xfffc, 0);
          return;
        }
        // examine selector to TSS, given in Task Gate descriptor
        // must specify global in the local/global bit else #GP(TSS selector)
        raw_tss_selector = descriptor.u.taskgate.tss_selector;
        parse_selector(raw_tss_selector, &tss_selector);
        if (tss_selector.ti) 
		{
          exception(IA32_GP_EXCEPTION, raw_tss_selector & 0xfffc, 0);
          return;
        }
        // index must be within GDT limits else #GP(TSS selector)
        fetch_raw_descriptor(&tss_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
        parse_descriptor(dword1, dword2, &tss_descriptor);
        if (tss_descriptor.valid==0 || tss_descriptor.segment) 
		{
          exception(IA32_GP_EXCEPTION, raw_tss_selector & 0xfffc, 0);
        }
        if (tss_descriptor.type!=9 && tss_descriptor.type!=1) 
		{
          exception(IA32_GP_EXCEPTION, raw_tss_selector & 0xfffc, 0);
        }
        // task state segment must be present, else #NP(tss selector)
        if (tss_descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, raw_tss_selector & 0xfffc, 0);
        }

        // SWITCH_TASKS _without_ nesting to TSS
        task_switch(&tss_selector, &tss_descriptor,IA32_TASK_FROM_JUMP, dword1, dword2);

        // EIP must be within code segment limit, else #GP(0)
        if ( sregs[IA32_SEG_REG_CS].cache.u.segment.d_b)
          temp_eIP = EIP;
        else
          temp_eIP =  IP;

        if (temp_eIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
        }
        break;
      case IA32_386_CALL_GATE:
        // gate must be present else #NP(gate selector)
        if (descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, cs_raw & 0xfffc, 0);
          return;
        }
        // examine selector to code segment given in call gate descriptor
        // selector must not be null, else #GP(0)
        gate_cs_raw = descriptor.u.gate386.dest_selector;
        if ((gate_cs_raw & 0xfffc) == 0) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
        }
        parse_selector(gate_cs_raw, &gate_cs_selector);
        // selector must be within its descriptor table limits else #GP(CS selector)
        fetch_raw_descriptor(&gate_cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
        parse_descriptor(dword1, dword2, &gate_cs_descriptor);
        // descriptor AR byte must indicate code segment else #GP(CS selector)
        if ( (gate_cs_descriptor.valid==0) ||  (gate_cs_descriptor.segment==0) || (gate_cs_descriptor.u.segment.executable==0) )
        {
          exception(IA32_GP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
        }
        // if non-conforming, code segment descriptor DPL must = CPL else #GP(CS selector)
        if (gate_cs_descriptor.u.segment.c_ed==0) 
		{
          if (gate_cs_descriptor.dpl != CPL) 
		  {
            exception(IA32_GP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
          }
        }
        // if conforming, then code segment descriptor DPL must <= CPL else #GP(CS selector)
        else 
		{
          if (gate_cs_descriptor.dpl > CPL) 
		  {
            exception(IA32_GP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
          }
        }

        // code segment must be present else #NP(CS selector)
        if (gate_cs_descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, gate_cs_raw & 0xfffc, 0);
        }

        // IP must be in code segment limit else #GP(0)
        if ( descriptor.u.gate386.dest_offset >  gate_cs_descriptor.u.segment.limit_scaled )
        {
          exception(IA32_GP_EXCEPTION, 0, 0);
        }

        load_cs(&gate_cs_selector, &gate_cs_descriptor, CPL);
        EIP = descriptor.u.gate386.dest_offset;
        return;
      default:
        exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
        return;
    }
  }
}

void  IA32_CPU::call_protected(Ia32_Instruction_c *i, Bit16u cs_raw, ia32_address dispBig)
{
  ia32_selector_t cs_selector;
  Bit32u dword1, dword2;
  ia32_descriptor_t cs_descriptor;

  /* new cs selector must not be null, else #GP(0) */
  if ((cs_raw & 0xfffc) == 0) 
  {
    exception(IA32_GP_EXCEPTION, 0, 0);
  }

  parse_selector(cs_raw, &cs_selector);
  fetch_raw_descriptor(&cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
  parse_descriptor(dword1, dword2, &cs_descriptor);
  // examine AR byte of selected descriptor for various legal values
  if (cs_descriptor.valid==0) 
  {
    exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
  }

  if (cs_descriptor.segment) 
  { // normal segment
    Bit32u temp_ESP;
    if (cs_descriptor.u.segment.executable==0) 
	{
      exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
      return;
    }

    if (cs_descriptor.u.segment.c_ed) 
	{ // conforming code segment
      // DPL must be <= CPL, else #GP(code seg selector)
      if (cs_descriptor.dpl > CPL) 
	  {
        exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
        return;
      }
    }
    else 
	{ // non-conforming code segment
      if ( (cs_selector.rpl > CPL) || (cs_descriptor.dpl != CPL) )
      {
        exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
      }
    }

    // segment must be present, else #NP(code seg selector)
    if (cs_descriptor.p == 0) 
	{
      exception(IA32_NP_EXCEPTION, cs_raw & 0xfffc, 0);
    }

    if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
      temp_ESP = ESP;
    else
      temp_ESP = SP;

    if (i->os32L()) 
	{
      if ( !can_push(& sregs[IA32_SEG_REG_SS].cache, temp_ESP, 8) ) 
	  {
        exception(IA32_SS_EXCEPTION, 0, 0);
      }

      // IP must be in code seg limit, else #GP(0)
      if (dispBig > cs_descriptor.u.segment.limit_scaled) 
	  {
        exception(IA32_GP_EXCEPTION, 0, 0);
      }

      push_32((Bit32u)  sregs[IA32_SEG_REG_CS].selector.value);
      push_32(EIP);
    }
    else 
	{ // 16bit opsize
      if ( !can_push(& sregs[IA32_SEG_REG_SS].cache, temp_ESP, 4) ) 
	  {
        exception(IA32_SS_EXCEPTION, 0, 0);
      }

      // IP must be in code seg limit, else #GP(0)
      if (dispBig > cs_descriptor.u.segment.limit_scaled) 
	  {
        exception(IA32_GP_EXCEPTION, 0, 0);
      }

      push_16( sregs[IA32_SEG_REG_CS].selector.value);
      push_16(IP);
    }

    load_cs(&cs_selector, &cs_descriptor, CPL);
    EIP = dispBig;
    if (cs_descriptor.u.segment.d_b==0)
      EIP &= 0x0000ffff;

    return;
  }
  else 
  { // gate & special segment
    ia32_descriptor_t  gate_descriptor;
    ia32_selector_t    gate_selector;
    Bit32u new_EIP;
    Bit16u dest_selector;
    Bit16u          raw_tss_selector;
    ia32_selector_t   tss_selector;
    ia32_descriptor_t tss_descriptor;
    Bit32u temp_eIP;

    /* 1 level of indirection via gate, switch gate & cs */
    gate_descriptor = cs_descriptor;
    gate_selector   = cs_selector;
    // descriptor DPL must be >= CPL else #GP(gate selector)
    if (gate_descriptor.dpl < CPL) 
	{
      exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
      return;
    }

    // descriptor DPL must be >= gate selector RPL else #GP(gate selector)
    if (gate_descriptor.dpl < gate_selector.rpl) 
	{
      exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
      return;
    }

    switch (gate_descriptor.type) 
	{
      case IA32_SYS_SEGMENT_AVAIL_286_TSS:
      case IA32_SYS_SEGMENT_AVAIL_386_TSS:
        task_switch(&gate_selector, &gate_descriptor,IA32_TASK_FROM_CALL_OR_INT, dword1, dword2);
        // EIP must be in code seg limit, else #GP(0)
        if (EIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
        }
        return;
      case IA32_TASK_GATE:
        // task gate must be present else #NP(gate selector)
        if (gate_descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, cs_raw & 0xfffc, 0);
          return;
        }

        raw_tss_selector = gate_descriptor.u.taskgate.tss_selector;
        parse_selector(raw_tss_selector, &tss_selector);
        if (tss_selector.ti) 
		{
          exception(IA32_TS_EXCEPTION, raw_tss_selector & 0xfffc, 0);
          return;
        }

        // index must be within GDT limits else #TS(TSS selector)
        fetch_raw_descriptor(&tss_selector, &dword1, &dword2, IA32_TS_EXCEPTION);
        parse_descriptor(dword1, dword2, &tss_descriptor);
        
		if (tss_descriptor.valid==0 || tss_descriptor.segment) 
		{
          exception(IA32_TS_EXCEPTION, raw_tss_selector & 0xfffc, 0);
        }
        
		if (tss_descriptor.type!=9 && tss_descriptor.type!=1) 
		{
          exception(IA32_TS_EXCEPTION, raw_tss_selector & 0xfffc, 0);
        }

        // task state segment must be present, else #NP(tss selector)
        if (tss_descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, raw_tss_selector & 0xfffc, 0);
        }

        task_switch(&tss_selector, &tss_descriptor,IA32_TASK_FROM_CALL_OR_INT, dword1, dword2);

        if ( sregs[IA32_SEG_REG_CS].cache.u.segment.d_b)
          temp_eIP = EIP;
        else
          temp_eIP =  IP;

        if (temp_eIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled)
        {
          exception(IA32_GP_EXCEPTION, 0, 0);
        }
        return;
      case IA32_286_CALL_GATE:
      case IA32_386_CALL_GATE:
        if (gate_descriptor.p==0) 
		{
          exception(IA32_NP_EXCEPTION, gate_selector.value & 0xfffc, 0);
        }

        // examine code segment selector in call gate descriptor
        if (gate_descriptor.type==IA32_286_CALL_GATE) 
		{
          dest_selector = gate_descriptor.u.gate286.dest_selector;
          new_EIP = gate_descriptor.u.gate286.dest_offset;
        }
        else 
		{
          dest_selector = gate_descriptor.u.gate386.dest_selector;
          new_EIP = gate_descriptor.u.gate386.dest_offset;
        }

        // selector must not be null else #GP(0)
        if ( (dest_selector & 0xfffc) == 0 ) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
        }

        parse_selector(dest_selector, &cs_selector);
        fetch_raw_descriptor(&cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
        parse_descriptor(dword1, dword2, &cs_descriptor);

        if (cs_descriptor.valid==0 || cs_descriptor.segment==0 || cs_descriptor.u.segment.executable==0 || cs_descriptor.dpl > CPL)
        {
          exception(IA32_GP_EXCEPTION, cs_selector.value & 0xfffc, 0);
        }

        if ( (cs_descriptor.u.segment.c_ed==0)  && (cs_descriptor.dpl < CPL) )
        {
          Bit16u SS_for_cpl_x;
          Bit32u ESP_for_cpl_x;
          ia32_selector_t   ss_selector;
          ia32_descriptor_t ss_descriptor;
          unsigned room_needed;
          Bit8u    param_count;
          Bit16u   return_SS, return_CS;
          Bit32u   return_ESP, return_EIP;
          Bit32u   return_ss_base;
          unsigned i;
          Bit16u   parameter_word[32];
          Bit32u   parameter_dword[32];
          Bit32u   temp_ESP;

           except_chk = 1;  
           except_cs =  sregs[IA32_SEG_REG_CS].selector.value;
           except_ss =  sregs[IA32_SEG_REG_SS].selector.value;
          
		  get_SS_ESP_from_TSS(cs_descriptor.dpl, &SS_for_cpl_x, &ESP_for_cpl_x);
          if ( (SS_for_cpl_x & 0xfffc) == 0 ) 
		  {
            //BX_PANIC(("call_protected: new SS null"));
            exception(IA32_TS_EXCEPTION, 0, 0);
            return;
          }

          parse_selector(SS_for_cpl_x, &ss_selector);
          fetch_raw_descriptor(&ss_selector, &dword1, &dword2, IA32_TS_EXCEPTION);
          parse_descriptor(dword1, dword2, &ss_descriptor);

          if (ss_selector.rpl != cs_descriptor.dpl) 
		  {
            exception(IA32_TS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
            return;
          }

          if (ss_descriptor.dpl != cs_descriptor.dpl) 
		  {
            exception(IA32_TS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
            return;
          }

          if (ss_descriptor.valid==0 || ss_descriptor.segment==0  || ss_descriptor.u.segment.executable || ss_descriptor.u.segment.r_w==0)
          {
            exception(IA32_TS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
            return;
          }

          if (ss_descriptor.p==0) 
		  {
            exception(IA32_SS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
            return;
          }

          if ( cs_descriptor.u.segment.d_b )
            room_needed = 16;
          else
            room_needed =  8;

          if (gate_descriptor.type==IA32_286_CALL_GATE) 
		  {
            param_count = gate_descriptor.u.gate286.word_count & 0x1f;
            room_needed += param_count*2;
          }
          else 
		  {
            param_count = gate_descriptor.u.gate386.dword_count & 0x1f;
            room_needed += param_count*4;
          }

          if ( !can_push(&ss_descriptor, ESP_for_cpl_x, room_needed) ) 
		  {
            exception(IA32_SS_EXCEPTION, SS_for_cpl_x & 0xfffc, 0);
            return;
          }

          if ( new_EIP > cs_descriptor.u.segment.limit_scaled ) 
		  {
            exception(IA32_GP_EXCEPTION, 0, 0);
            return;
          }

          return_SS =  sregs[IA32_SEG_REG_SS].selector.value;
          if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
            return_ESP = ESP;
          else
            return_ESP =  SP;
          return_ss_base =  get_segment_base(IA32_SEG_REG_SS);

          // save return CS:eIP to be pushed on new stack
          return_CS =  sregs[IA32_SEG_REG_CS].selector.value;
          if ( cs_descriptor.u.segment.d_b )
            return_EIP = EIP;
          else
            return_EIP = IP;

          if (gate_descriptor.type==IA32_286_CALL_GATE) 
		  {
            for (i=0; i<param_count; i++) 
			{
                m_vm32->access_address_n(this,return_ss_base + return_ESP + i*2, 2, 0, IA32_READ, &parameter_word[i]);
#ifdef IA32_BIG_ENDIAN
                IA32_ReadHostWordFromLittleEndian(&(parameter_word[i]), &(parameter_word[i]));
#endif//IA32_BIG_ENDIAN
            }
          }
          else 
		  {
            for (i=0; i<param_count; i++) 
			{
                m_vm32->access_address_n(this,return_ss_base + return_ESP + i*4,4, 0, IA32_READ, &parameter_dword[i]);
#ifdef IA32_BIG_ENDIAN
                IA32_ReadHostDWordFromLittleEndian(&(parameter_dword[i]), &(parameter_dword[i]));
#endif//IA32_BIG_ENDIAN
            }
          }

          load_ss(&ss_selector, &ss_descriptor, ss_descriptor.dpl);
          if (ss_descriptor.u.segment.d_b)
            ESP = ESP_for_cpl_x;
          else
            SP =  (Bit16u) ESP_for_cpl_x;

          load_cs(&cs_selector, &cs_descriptor, cs_descriptor.dpl);
          EIP = new_EIP;

          if (gate_descriptor.type==IA32_286_CALL_GATE) 
		  {
            push_16(return_SS);
            push_16((Bit16u) return_ESP);
          }
          else 
		  {
            push_32(return_SS);
            push_32(return_ESP);
          }

          if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
            temp_ESP = ESP;
          else
            temp_ESP =  SP;

          if (gate_descriptor.type==IA32_286_CALL_GATE) 
		  {
            for (i=param_count; i>0; i--) 
			{
              push_16(parameter_word[i-1]);
            }
          }
          else 
		  {
            for (i=param_count; i>0; i--) 
			{
              push_32(parameter_dword[i-1]);
            }
          }

          if (gate_descriptor.type==IA32_286_CALL_GATE) 
		  {
            push_16(return_CS);
            push_16((Bit16u) return_EIP);
          }
          else 
		  {
            push_32(return_CS);
            push_32(return_EIP);
          }

           except_chk = 0;
          return;
        }
        else 
		{
          Bit32u temp_ESP;

          if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
            temp_ESP = ESP;
          else
            temp_ESP = SP;

          if (gate_descriptor.type==IA32_386_CALL_GATE) 
		  {
            if ( !can_push(& sregs[IA32_SEG_REG_SS].cache, temp_ESP, 8) ) 
			{
              exception(IA32_SS_EXCEPTION, 0, 0);
            }
          }
          else 
		  {
            if ( !can_push(& sregs[IA32_SEG_REG_SS].cache, temp_ESP, 4) ) 
			{
              exception(IA32_SS_EXCEPTION, 0, 0);
            }
          }

          if ( new_EIP > cs_descriptor.u.segment.limit_scaled ) 
		  {
            exception(IA32_GP_EXCEPTION, 0, 0);
          }

          if (gate_descriptor.type==IA32_386_CALL_GATE) 
		  {
            push_32( sregs[IA32_SEG_REG_CS].selector.value);
            push_32(EIP);
          }
          else 
		  {
            push_16( sregs[IA32_SEG_REG_CS].selector.value);
            push_16(IP);
          }

          load_cs(&cs_selector, &cs_descriptor, CPL);
          EIP = new_EIP;
          return;
        }
        return;
      default:
        exception(IA32_GP_EXCEPTION, cs_raw & 0xfffc, 0);
        return;
    }
  }

}

void  IA32_CPU::return_protected(Ia32_Instruction_c *i, Bit16u pop_bytes)
{
  Bit16u raw_cs_selector, raw_ss_selector;
  ia32_selector_t cs_selector, ss_selector;
  ia32_descriptor_t cs_descriptor, ss_descriptor;
  Bit32u stack_cs_offset, stack_param_offset;
  Bit32u return_EIP, return_ESP, temp_ESP;
  Bit32u dword1, dword2;
  Bit16u return_IP;


  /* + 6+N*2: SS      | +12+N*4:     SS */
  /* + 4+N*2: SP      | + 8+N*4:    ESP */
  /*          parm N  | +        parm N */
  /*          parm 3  | +        parm 3 */
  /*          parm 2  | +        parm 2 */
  /*          parm 1  | + 8:     parm 1 */
  /* + 2:     CS      | + 4:         CS */
  /* + 0:     IP      | + 0:        EIP */

  if ( i->os32L() ) 
  {
    if (!can_pop(6)) 
	{
      exception(IA32_SS_EXCEPTION, 0, 0);
      return;
    }
    stack_cs_offset = 4;
    stack_param_offset = 8;
  }
  else 
  {
    if ( !can_pop(4) ) 
	{
      exception(IA32_SS_EXCEPTION, 0, 0);
      return;
    }
    stack_cs_offset = 2;
    stack_param_offset = 4;
  }

  if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b) 
		temp_ESP = ESP;
	else 
		temp_ESP = SP;
 
  m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + stack_cs_offset, 2, CPL==3, IA32_READ, &raw_cs_selector);
#ifdef  IA32_BIG_ENDIAN
    IA32_ReadHostWordFromLittleEndian(&raw_cs_selector, &raw_cs_selector);
#endif
  parse_selector(raw_cs_selector, &cs_selector);

  if ( cs_selector.rpl < CPL ) 
  {
    exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
    return;
  }

  if ( cs_selector.rpl == CPL ) 
  {
    if ( (raw_cs_selector & 0xfffc) == 0 ) 
	{
      exception(IA32_GP_EXCEPTION, 0, 0);
      return;
    }

    fetch_raw_descriptor(&cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
    parse_descriptor(dword1, dword2, &cs_descriptor);

    if (cs_descriptor.valid==0 || cs_descriptor.segment==0 || cs_descriptor.u.segment.executable==0)
    {
      exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
    }

    if (cs_descriptor.u.segment.c_ed)
    {
      if (cs_descriptor.dpl>CPL) 
	  {
        exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
        return;
      }
    }
    else 
	{
      if (cs_descriptor.dpl != CPL)
	  {
        exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
        return;
      }
    }

    if (cs_descriptor.p==0) 
	{
      exception(IA32_NP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if ( !can_pop(stack_param_offset + pop_bytes) )
	{
      exception(IA32_SS_EXCEPTION, 0, 0); /* #SS(0) */
      return;
    }

    if (i->os32L()) 
	{
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0, 4, CPL==3, IA32_READ, &return_EIP);
#ifdef IA32_BIG_ENDIAN
      IA32_ReadHostDWordFromLittleEndian(&return_EIP, &return_EIP);
#endif//IA32_BIG_ENDIAN
    }
    else 
	{
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0, 2, CPL==3, IA32_READ, &return_IP);
#ifdef IA32_BIG_ENDIAN
      IA32_ReadHostWordFromLittleEndian(&return_IP, &return_IP);
#endif//IA32_BIG_ENDIAN
      return_EIP = return_IP;
    }

    if ( return_EIP > cs_descriptor.u.segment.limit_scaled ) 
	{
      exception(IA32_GP_EXCEPTION, 0, 0);
      return;
    }

    load_cs(&cs_selector, &cs_descriptor, CPL);
    EIP = return_EIP;
    
	if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
         ESP += stack_param_offset + pop_bytes;
    else
         SP += stack_param_offset + pop_bytes;

    return;
  }
  else 
  {
	  if (i->os32L()) 
	  {
		if ( !can_pop(16 + pop_bytes) ) 
		{
			exception(IA32_SS_EXCEPTION, 0, 0); /* #SS(0) */
			return;
		}
      }
	  else 
	  {
		if ( !can_pop(8 + pop_bytes) ) 
		{
			exception(IA32_SS_EXCEPTION, 0, 0); /* #SS(0) */
			return;
		}
      }


    if ( (raw_cs_selector & 0xfffc) == 0 ) 
	{
      exception(IA32_GP_EXCEPTION, 0, 0);
      return;
    }

    fetch_raw_descriptor(&cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
    parse_descriptor(dword1, dword2, &cs_descriptor);
    if (cs_descriptor.valid==0 || cs_descriptor.segment==0  || cs_descriptor.u.segment.executable==0)
    {
      exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if (cs_descriptor.u.segment.c_ed) 
	{
       if (cs_descriptor.dpl > cs_selector.rpl) 
	   {
        exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
        return;
      }
    }
    else 
	{
      if (cs_descriptor.dpl != cs_selector.rpl)
	  {
        exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
        return;
      }
    }

    if (cs_descriptor.p==0) 
	{
      exception(IA32_NP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if (i->os32L()) 
	{
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 12 + pop_bytes, 2, 0, IA32_READ, &raw_ss_selector);
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 8 + pop_bytes,  4, 0, IA32_READ, &return_ESP);
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0, 4, 0, IA32_READ, &return_EIP);
#ifdef IA32_BIG_ENDIAN
      IA32_ReadHostWordFromLittleEndian(&raw_ss_selector, &raw_ss_selector);
      IA32_ReadHostDWordFromLittleEndian(&return_ESP, &return_ESP);
      IA32_ReadHostDWordFromLittleEndian(&return_EIP, &return_EIP);
#endif//IA32_BIG_ENDIAN
    }
    else 
	{
      Bit16u return_SP;
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 6 + pop_bytes, 2, 0, IA32_READ, &raw_ss_selector);
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 4 + pop_bytes, 2, 0, IA32_READ, &return_SP);
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0, 2, 0, IA32_READ, &return_IP);
#ifdef IA32_BIG_ENDIAN
      IA32_ReadHostWordFromLittleEndian(&raw_ss_selector, &raw_ss_selector);
      IA32_ReadHostWordFromLittleEndian(&return_SP, &return_SP);
      IA32_ReadHostWordFromLittleEndian(&return_IP, &return_IP);
#endif//IA32_BIG_ENDIAN
      return_ESP = return_SP;
      return_EIP = return_IP;
    }

    if ( (raw_ss_selector & 0xfffc) == 0 ) 
	{
      exception(IA32_GP_EXCEPTION, 0, 0);
      return;
    }

    parse_selector(raw_ss_selector, &ss_selector);
    fetch_raw_descriptor(&ss_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
    parse_descriptor(dword1, dword2, &ss_descriptor);
    if (ss_selector.rpl != cs_selector.rpl) 
	{
      exception(IA32_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0);
      return;
    }

    if (ss_descriptor.valid==0 || ss_descriptor.segment==0 || ss_descriptor.u.segment.executable || ss_descriptor.u.segment.r_w==0)
    {
      exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if (ss_descriptor.dpl != cs_selector.rpl) 
	{
      exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if (ss_descriptor.p==0) 
	{
      exception(IA32_SS_EXCEPTION, raw_ss_selector & 0xfffc, 0);
      return;
    }

    if (return_EIP > cs_descriptor.u.segment.limit_scaled) 
	{
      exception(IA32_GP_EXCEPTION, 0, 0);
      return;
    }

    load_cs(&cs_selector, &cs_descriptor, cs_selector.rpl);
    EIP = return_EIP;
    load_ss(&ss_selector, &ss_descriptor, cs_selector.rpl);
    
	if (ss_descriptor.u.segment.d_b)
      ESP = return_ESP + pop_bytes;
    else
      SP  = (Bit16u) return_ESP + pop_bytes;

    validate_seg_regs();
  }

}

void   IA32_CPU::iret_protected(Ia32_Instruction_c *i)
{
  Bit16u raw_cs_selector, raw_ss_selector;
  ia32_selector_t cs_selector, ss_selector;
  Bit32u dword1, dword2;
  ia32_descriptor_t cs_descriptor, ss_descriptor;

  if (  get_NT () ) 
  { /* NT = 1: RETURN FROM NESTED TASK */
    /* what's the deal with NT & VM ? */
    Bit32u base32;
    Bit16u raw_link_selector;
    ia32_selector_t   link_selector;
    ia32_descriptor_t tss_descriptor;

    if ( tr.cache.type == IA32_SYS_SEGMENT_AVAIL_286_TSS)
      base32 =  tr.cache.u.tss286.base;
    else 
		if ( tr.cache.type == IA32_SYS_SEGMENT_AVAIL_386_TSS)
			base32 =  tr.cache.u.tss386.base;
		else 
		{
			base32 = 0; // keep compiler happy
		}

    m_vm32->access_address_n(this,base32 + 0, 2, 0, IA32_READ, &raw_link_selector);
#ifdef IA32_BIG_ENDIAN
    IA32_ReadHostWordFromLittleEndian(&raw_link_selector, &raw_link_selector);
#endif//IA32_BIG_ENDIAN
    parse_selector(raw_link_selector, &link_selector);
    if (link_selector.ti) 
	{
      exception(IA32_TS_EXCEPTION, raw_link_selector & 0xfffc, 0);
    }

    fetch_raw_descriptor(&link_selector, &dword1, &dword2, IA32_TS_EXCEPTION);
    parse_descriptor(dword1, dword2, &tss_descriptor);
    if (tss_descriptor.valid==0 || tss_descriptor.segment) 
	{
      exception(IA32_TS_EXCEPTION, raw_link_selector & 0xfffc, 0);
    }
    
	if ((tss_descriptor.type!=11) && (tss_descriptor.type!=3)) 
	{
      exception(IA32_TS_EXCEPTION, raw_link_selector & 0xfffc, 0);
    }

    if (tss_descriptor.p==0) 
	{
      exception(IA32_NP_EXCEPTION, raw_link_selector & 0xfffc, 0);
    }

    task_switch(&link_selector, &tss_descriptor,IA32_TASK_FROM_IRET, dword1, dword2);
    if (EIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled) 
	{
      exception(IA32_GP_EXCEPTION, 0, 0);
    }
    return;
  }
  else 
  {
    /* NT = 0: INTERRUPT RETURN ON STACK -or STACK_RETURN_TO_V86 */
    Bit16u top_nbytes_same, top_nbytes_outer;
    Bit32u cs_offset, ss_offset;
    Bit32u new_eip, new_esp, temp_ESP, new_eflags;
    Bit16u new_ip, new_sp, new_flags;
    Bit8u prev_cpl;

    if (i->os32L()) 
	{
      top_nbytes_same    = 12;
      top_nbytes_outer   = 20;
      cs_offset = 4;
      ss_offset = 16;
    }
    else 
	{
      top_nbytes_same    = 6;
      top_nbytes_outer   = 10;
      cs_offset = 2;
      ss_offset = 8;
    }

    /* CS on stack must be within stack limits, else #SS(0) */
    if ( !can_pop(top_nbytes_same) ) 
	{
      exception(IA32_SS_EXCEPTION, 0, 0);
      return;
    }

    if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
      temp_ESP = ESP;
    else
      temp_ESP = SP;

    m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + cs_offset, 2, CPL==3, IA32_READ, &raw_cs_selector);
#ifdef IA32_BIG_ENDIAN
    IA32_ReadHostWordFromLittleEndian(&raw_cs_selector, &raw_cs_selector);
#endif//IA32_BIG_ENDIAN
    if (i->os32L()) 
	{
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0,4, CPL==3, IA32_READ, &new_eip);
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 8,4, CPL==3, IA32_READ, &new_eflags);
#ifdef IA32_BIG_ENDIAN
      IA32_ReadHostDWordFromLittleEndian(&new_eip, &new_eip);
      IA32_ReadHostDWordFromLittleEndian(&new_eflags, &new_eflags);
#endif//IA32_BIG_ENDIAN
      if (new_eflags & 0x00020000) 
	  {
        if (CPL == 0) 
		{
           stack_return_to_v86(new_eip, raw_cs_selector, new_eflags);
          return;
        }
      }
    }
    else 
	{
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0, 2, CPL==3, IA32_READ, &new_ip);
      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 4, 2, CPL==3, IA32_READ, &new_flags);
#ifdef IA32_BIG_ENDIAN
      IA32_ReadHostWordFromLittleEndian(&new_ip, &new_ip);
      IA32_ReadHostWordFromLittleEndian(&new_flags, &new_flags);
#endif//IA32_BIG_ENDIAN
    }

    parse_selector(raw_cs_selector, &cs_selector);
    if ( (raw_cs_selector & 0xfffc) == 0 ) 
	{
      exception(IA32_GP_EXCEPTION, 0, 0);
      return;
    }

    fetch_raw_descriptor(&cs_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
    parse_descriptor(dword1, dword2, &cs_descriptor);
    if ( cs_descriptor.valid==0 || cs_descriptor.segment==0  || cs_descriptor.u.segment.executable==0 )
    {
      exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if (cs_selector.rpl < CPL) 
	{
      exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if (cs_descriptor.u.segment.c_ed)
    {
      if (cs_descriptor.dpl > cs_selector.rpl) 
	  {
        exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
        return;
      }
    }
    else 
	{
      if (cs_descriptor.dpl != cs_selector.rpl) 
	  {
        exception(IA32_GP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
        return;
      }
    }

    if ( cs_descriptor.p==0 )
	{
      exception(IA32_NP_EXCEPTION, raw_cs_selector & 0xfffc, 0);
      return;
    }

    if (cs_selector.rpl == CPL) 
	{ 
      if (i->os32L()) 
	  {
        if ( new_eip > cs_descriptor.u.segment.limit_scaled ) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
          return;
        }

		load_cs(&cs_selector, &cs_descriptor, CPL);
        EIP = new_eip;
        write_eflags(new_eflags, CPL==0, CPL<= get_IOPL (), 0, 1);
      }
      else 
	  {
        if ( new_ip > cs_descriptor.u.segment.limit_scaled ) 
		{
          exception(IA32_GP_EXCEPTION, 0, 0);
          return;
        }
 
		load_cs(&cs_selector, &cs_descriptor, CPL);
        EIP = new_ip;
        write_flags(new_flags, CPL==0, CPL<= get_IOPL ());
      }

      if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
        ESP += top_nbytes_same;
      else
        SP += top_nbytes_same;
      return;
    }
    else 
	{ 
      if ( !can_pop(top_nbytes_outer) ) 
	  {
        exception(IA32_SS_EXCEPTION, 0, 0);
        return;
      }

      m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + ss_offset, 2, 0, IA32_READ, &raw_ss_selector);
#ifdef IA32_BIG_ENDIAN
      IA32_ReadHostWordFromLittleEndian(&raw_ss_selector, &raw_ss_selector);
#endif
      if ( (raw_ss_selector & 0xfffc) == 0 ) 
	  {
        exception(IA32_GP_EXCEPTION, 0, 0);
        return;
      }

      parse_selector(raw_ss_selector, &ss_selector);
      if ( ss_selector.rpl != cs_selector.rpl) 
	  {
        exception(IA32_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0);
        return;
      }

      fetch_raw_descriptor(&ss_selector, &dword1, &dword2, IA32_GP_EXCEPTION);
      parse_descriptor(dword1, dword2, &ss_descriptor);

	  if ( ss_descriptor.valid==0 || ss_descriptor.segment==0  || ss_descriptor.u.segment.executable  || ss_descriptor.u.segment.r_w==0 )
      {
        exception(IA32_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0);
        return;
      }

      if ( ss_descriptor.dpl != cs_selector.rpl ) 
	  {
        exception(IA32_GP_EXCEPTION, raw_ss_selector & 0xfffc, 0);
        return;
      }

      if ( ss_descriptor.p==0 ) 
	  {
        exception(IA32_NP_EXCEPTION, raw_ss_selector & 0xfffc, 0);
        return;
      }

      if (i->os32L()) 
	  {
        m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0,4, 0, IA32_READ, &new_eip);
        m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 8,4, 0, IA32_READ, &new_eflags);
        m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 12,4, 0, IA32_READ, &new_esp);
#ifdef IA32_BIG_ENDIAN
        IA32_ReadHostDWordFromLittleEndian(&new_eip, &new_eip);
        IA32_ReadHostDWordFromLittleEndian(&new_eflags, &new_eflags);
        IA32_ReadHostDWordFromLittleEndian(&new_esp, &new_esp);
#endif
      }
      else 
	  {
        m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 0, 2, 0, IA32_READ, &new_ip);
        m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 4, 2, 0, IA32_READ, &new_flags);
        m_vm32->access_address_n(this, get_segment_base(IA32_SEG_REG_SS) + temp_ESP + 6, 2, 0, IA32_READ, &new_sp);
#ifdef IA32_BIG_ENDIAN
        IA32_ReadHostWordFromLittleEndian(&new_ip, &new_ip);
        IA32_ReadHostWordFromLittleEndian(&new_flags, &new_flags);
        IA32_ReadHostWordFromLittleEndian(&new_sp, &new_sp);
#endif
        new_eip = new_ip;
        new_esp = new_sp;
        new_eflags = new_flags;
      }

      if ( new_eip > cs_descriptor.u.segment.limit_scaled ) 
	  {
        exception(IA32_GP_EXCEPTION, 0, 0);
        return;
      }

      prev_cpl = CPL; /* previous CPL */
      load_cs(&cs_selector, &cs_descriptor, cs_selector.rpl);
      EIP = new_eip;

      if (cs_descriptor.u.segment.d_b)
        write_eflags(new_eflags, prev_cpl==0, prev_cpl<= get_IOPL (), 0, 1);
      else
        write_flags((Bit16u) new_eflags, prev_cpl==0, prev_cpl<= get_IOPL ());

      load_ss(&ss_selector, &ss_descriptor, cs_selector.rpl);
     
	  if (ss_descriptor.u.segment.d_b)
        ESP = new_esp;
      else
        SP  = new_esp;

      validate_seg_regs();
      return;
    }
 
 }

}

void  IA32_CPU::branch_near32(Bit32u new_EIP)
{
  if (new_EIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled)
  {
    exception(IA32_GP_EXCEPTION, 0, 0);
  }
  EIP = new_EIP;
}


void IA32_CPU::validate_seg_regs(void)
{
  Bit8u cs_dpl =  sregs[IA32_SEG_REG_CS].cache.dpl;

  if( sregs[IA32_SEG_REG_ES].cache.dpl < cs_dpl)
  {
     sregs[IA32_SEG_REG_ES].cache.valid = 0;
     sregs[IA32_SEG_REG_ES].selector.value = 0;
  }
  
  if( sregs[IA32_SEG_REG_DS].cache.dpl< cs_dpl)
  {
     sregs[IA32_SEG_REG_DS].cache.valid = 0;
     sregs[IA32_SEG_REG_DS].selector.value = 0;
  }
  
  if( sregs[IA32_SEG_REG_FS].cache.dpl < cs_dpl)
  {
     sregs[IA32_SEG_REG_FS].cache.valid = 0;
     sregs[IA32_SEG_REG_FS].selector.value = 0;
  }
  
  if( sregs[IA32_SEG_REG_GS].cache.dpl < cs_dpl)
  {
     sregs[IA32_SEG_REG_GS].cache.valid = 0;
     sregs[IA32_SEG_REG_GS].selector.value = 0;
  }

}
